#pragma once

#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <cassert>
using namespace std;

template<class T>
class LoopQueue {
private:
    vector<T> _queue; // 用数组来模拟环形队列
    int _capacity; // 环形队列的最大容量
    sem_t _space_sem; // 空间信号量，记录队列中还有多少剩余空间
    sem_t _data_sem; // 数据信号量，记录队列中还有多少有效数据
    pthread_mutex_t _producer_mutex; // 保证生产者间的互斥关系
    pthread_mutex_t _consumer_mutex; // 保证消费者间的互斥关系
    int _producer_index; // 记录生产者存放数据时应访问的下标
    int _consumer_index; // 记录消费者读取数据时应访问的下标

public:
    LoopQueue(const int& capacity = 20)
        : _queue(capacity)
        , _capacity(capacity)
        , _producer_index(0)
        , _consumer_index(0)
    {
        sem_init(&_space_sem, 0, capacity);
        sem_init(&_data_sem, 0, 0);

        pthread_mutex_init(&_producer_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }

    ~LoopQueue() {
        sem_destroy(&_space_sem);
        sem_destroy(&_data_sem);

        pthread_mutex_destroy(&_producer_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }

private:
    void P(sem_t& sem) {
        int n = sem_wait(&sem);
        assert(n == 0);
        (void)n;
    }

    void V(sem_t& sem) {
        int n = sem_post(&sem);
        assert(n == 0);
        (void)n;
    }

public:
    void push(const T& in) {
        P(_space_sem);
        pthread_mutex_lock(&_producer_mutex);

        _queue[_producer_index++] = in;
        _producer_index %= _capacity;

        pthread_mutex_unlock(&_producer_mutex);
        V(_data_sem);
    }

    void pop(T* out) {
        P(_data_sem);
        pthread_mutex_lock(&_consumer_mutex);

        *out = _queue[_consumer_index++];
        _consumer_index %= _capacity;

        pthread_mutex_unlock(&_consumer_mutex);
        V(_space_sem);
    }
};